//#include <sys/dir.h>		// local directory structure
#include <sys/stat.h>		// structure returned by stat
#include <sys/types.h>		// typedefs
#include <fcntl.h>		// flags for read and write
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "dirent.h"

// fsize: print the name of file "name"
void fsize(char *name)
{
	struct stat stbuf;		// 存储i结点信息的结构，该结构的声明在头文件 <sys/stat.h> 中

	if(stat(name, &stbuf) == -1)	// 系统调用 stat 将 name 文件的 i 结点信息存储在 stbuf 中，
					// 如果返回值为 -1 表示出错
	{
		fprintf(stderr, "fsize: can't access %s\n", name);
		return ;
	}
	if((stbuf.st_mode & S_IFMT) == S_IFDIR)	 	// S_IFMT 表示文件类型，S_IFDIR 表示文件类型为目录
		dirwalk(name, fsize);			// 遍历目录
	
	printf("%lld\t%ld\t%d\t%d\t%d\t%d\t%lld\t%8ld\t%ld\t%ld\t%ld\t%s\n",
		stbuf.st_dev, stbuf.st_ino, stbuf.st_mode, stbuf.st_nlink, stbuf.st_uid, stbuf.st_gid, 
		stbuf.st_rdev, stbuf.st_size, stbuf.st_atime, stbuf.st_mtime, stbuf.st_ctime, name);
}

// dirwalk: apply fcn to all files in dir
void dirwalk(char *dir, void (*fun)(char *))
{
	char name[MAX_PATH];
	Dirent *dp;
	DIR *dfd;

	if((dfd = opendir(dir)) == NULL)	// opendir 函数打开目录，并将信息保存在一个DIR结构中返回，
						// 如果出现错误，返回NULL
	{
		fprintf(stderr, "dirwalk: can't open %s\n", dir);
		return ;
	}
	while((dp = readdir(dfd)) != NULL)	// 直接整个目录遍历完
	{
		// 如果返回的文件名为 "." 或 ".."，为目录本身和父目录，跳过
		if(strcmp(dp->name, ".") == 0 || strcmp(dp->name, "..") == 0)
			continue ;		// skip self and parent
		if(strlen(dir) + strlen(dp->name) + 2 > sizeof(name))		// 文件名过长
			fprintf(stderr, "dirwalk: name %s %s too long\n", dir, dp->name);
		else
		{
			sprintf(name, "%s/%s", dir, dp->name);
			(*fun)(name);		// 递归调用，查看文件是否为目录，如果为目录，继续递进，
						// 否则递归回归，继续下一个文件
		}
	}
	closedir(dfd);
}

// opendit: open a directory for readdir calls
DIR *opendir(char *dirname)
{
	int fd;		// 文件描述符
	struct stat stbuf;	// 存储i结点信息的结构
	DIR *dp;

	if((fd = open(dirname, O_RDONLY, 0)) == -1 	// open函数返回文件描述符
		|| fstat(fd, &stbuf) == -1		// 系统调用fstat打开文件，并将文件的i结点信息存储在stbuf中
		|| (stbuf.st_mode & S_IFMT) != S_IFDIR	// 如果文件类型不是目录
		|| (dp = (DIR *)malloc(sizeof(DIR))) == NULL)	// 如果分配内存失败
			return NULL;
	dp->fd = fd;
	return dp;
	
}

// closedir: close directory opened by opendir
void closedir(DIR *dp)
{
	if(dp)
	{
		close(dp->fd);		// 关闭文件
		free(dp);		// 释放分配的内存
	}
}

// readdir: read directory entries in sequence
Dirent *readdir(DIR *dp)
{
	struct direct dirbuf;		// local directoty structure
	// d 是一个 static 变量，每次退出函数不会销毁，用于储存目录中一个文件名和索引号
	static Dirent d;		// return: portable structure		
	// 每次从目录中读取一个 direct 结构的信息
	while(read(dp->fd, (char *) &dirbuf, sizeof(dirbuf)) == sizeof(dirbuf))
	{
		// 如果目录位置当前未使用，跳过，读取下一个
		if(dirbuf.d_ino == 0)	// slot not in use	
			continue;
		d.ino = dirbuf.d_ino;	// 将索引号与文件名拷贝
		strncpy(d.name, dirbuf.d_name, DIRSIZ);
		d.name[DIRSIZ] = '\0';	// 在文件名后添加空字符
		return &d;
	}	
	return NULL;	// 如果目录中信息读取完，就返回NULL
}
	
